package com.roundarch.www.util;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.set.ListOrderedSet;


/**
 * @author Sanjay Ginde
 *
 */
abstract public class CollectionUtils extends org.apache.commons.collections.CollectionUtils
{

    /**
     * Retrieves all values from the map using the supplied array of keys into a
     * parallel array of values.
     * 
     * @param hashMap
     *            The hashmap to query the values.
     * @param paramNames
     *            The key names.
     * @return The parallel array of values for the corresponding keynames.
     */
    public static Object[] getMapValues(Map<String, Object> map, String[] paramNames)
    {
        List<Object> list = new ArrayList<Object>();
        int paramCount = paramNames.length;
        for (int i = 0; i < paramCount; i++)
        {
            list.add(map.get(paramNames[i]));
        }
        return list.toArray();
    }

    /**
     * Converts an array of Objects into an array of Strings, by calling the
     * toString() method on each object.
     * 
     * @param objects
     *            The array of Objects to be converted.
     * @return An array of Strings.
     */
    public static String[] convertObjectArray(Object[] objects)
    {
        if (objects == null)
        {
            return new String[] {};
        }
        String[] strings = new String[objects.length];
        int numOfObjects = objects.length;
        for (int i = 0; i < numOfObjects; i++)
        {
            strings[i] = objects[i].toString();
        }
        return strings;
    }

    /**
     * Trims objects from the end of a ListOrderedSet until the size of the list
     * is the specified size. Does nothing if the newSize is greater than the
     * list's size.
     * 
     * @param set
     *            The ListOrderedSet to be trimmed.
     * @param newSize
     *            The new size of the set.
     */
    public static void trimOrderedSetToLength(ListOrderedSet listOrderedSet, int newSize)
    {
        if (newSize < 0 || listOrderedSet == null)
            return;

        if (newSize > listOrderedSet.size())
            return;

        while (listOrderedSet.size() > newSize)
        {
            listOrderedSet.remove(listOrderedSet.size() - 1);
        }
    }

    public static void merge(Collection<Object> existingCollection, Collection<Object> newCollection)
    {
        for (Iterator<Object> i = existingCollection.iterator(); i.hasNext();)
            if (!newCollection.contains(i.next()))
                i.remove();

        for (Iterator<Object> i = newCollection.iterator(); i.hasNext();)
            existingCollection.add(i.next());
    }

    public static void resizeCollection(Collection<Object> collection, int total)
    {
        if (collection.size() <= total)
            return;

        for (Iterator<Object> i = collection.iterator(); i.hasNext();)
        {
            i.next();
            if (--total < 0)
                i.remove();
        }
    }

    public static <T extends Comparable<? super T>> List<T> sort(List<T> collection)
    {
        if (collection == null)
            return collection;

        Collections.sort(collection);
        return collection;
    }

    public static <T extends Object, S extends T> List<S> sort(List<S> collection, Comparator<T> comparator)
    {
        if (collection == null)
            return collection;

        Collections.sort(collection, comparator);
        return collection;
    }

    public static boolean hasValues(Collection<? extends Object> collection)
    {
        return (collection != null && collection.size() > 0);
    }

    public static boolean isEmpty(Object[] array)
    {
        return array == null || array.length == 0;
    }

    public static boolean isNotEmpty(Object[] array)
    {
        return !CollectionUtils.isEmpty(array);
    }

    /**
     * Null safe check on the collection. Returns null if collection is empty.
     */
    public static <T> T getFirst(Collection<T> collection)
    {
        if (isEmpty(collection))
            return null;

        return collection.iterator().next();
    }

    @SuppressWarnings("unchecked")
    public static <T> List<T> pluck(List<?> list, String property, Class<T> propertyType) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException
    {
        if (list == null)
            return null;

        List<T> result = new ArrayList<T>(list.size());
        for (Object o : list)
            result.add((T) PropertyUtils.getProperty(o, property));

        return result;
    }

    public static <T> List<T> distinctList(List<T> list)
    {
        List<T> result = new ArrayList<T>(list.size());
        Set<T> distinct = new LinkedHashSet<T>(list);
        result.addAll(distinct);

        return result;
    }

    public static <T> List<T> createList(T... elements)
    {
        List<T> l = new ArrayList<T>();
        Collections.addAll(l, elements);
        return l;
    }
}
